package sk.tuke.magsa.tools.builder;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import sk.tuke.magsa.tools.metamodel.Entity;
import sk.tuke.magsa.tools.metamodel.Model;
import sk.tuke.magsa.tools.metamodel.Property;
import sk.tuke.magsa.tools.metamodel.constraints.Capital;
import sk.tuke.magsa.tools.metamodel.constraints.Constraint;
import sk.tuke.magsa.tools.metamodel.constraints.EntityRef;
import sk.tuke.magsa.tools.metamodel.constraints.Length;
import sk.tuke.magsa.tools.metamodel.constraints.PropertyRef;
import sk.tuke.magsa.tools.metamodel.constraints.Required;
import sk.tuke.magsa.tools.metamodel.constraints.Range;
import sk.tuke.magsa.tools.metamodel.constraints.Regex;

//SEE ALSO: Nested Function
//http://martinfowler.com/dslwip/NestedFunction.html
public abstract class ConstraintBuilder {
    private List<EntityRef> entities = new ArrayList<EntityRef>();
    private String propertyName = null;
    private Constraint[] cons;
    
    
    private Model model;

    protected abstract void define();

    public void compose(Model model) throws ConstraintProcessingException {
        this.model = model;
        define();
        validate();
    }

    protected void entity_ref(String name, PropertyRef... properties) {
        model.findEntity(name).findProperty(propertyName).setConstraints(cons);
        entities.add(new EntityRef(name, properties));
    }

    protected PropertyRef property_ref(String name, Constraint... constraints) {
        this.propertyName = name;
        this.cons = constraints;
        return new PropertyRef(name, constraints);
    }

    protected Required required() {
        return new Required();
    }

    private void validate() throws ConstraintProcessingException {
        Boolean eBool = false;
        Boolean pBool = false;
        String pName = null;
        String eName = null;
        String pEName = null;
        
        for(EntityRef er : entities)
        {
            for(Entity e : model.getEntities())
            {
                if(er.getName().equals(e.getName()))
                {
                    eBool = true;
                    for(PropertyRef pr : er.getProperties())
                    {
                        for(Property p : e.getProperties())
                        {
                            if(pr.getName().equals(p.getName()))
                                pBool = true;
                            else
                            {
                                pName = pr.getName();
                                pEName = e.getName();
                            }
                        }
                    }
                }
                else 
                {
                    eName = er.getName();
                }
            }
        }
        if(eBool == false)
            throw new ConstraintProcessingException("\"" + eName + "\" is not correct name of Entity");
        else if(pBool == false)
            throw new ConstraintProcessingException("\"" + pName + "\" is not correct property name of \"" + pEName + "\" entity");
    }
    
    protected Length length(int minLength, int maxLength)
    {
        return new Length(minLength, maxLength);
    }
    
    protected Length min_length(int minLength)
    {
        return new Length(minLength, Integer.MAX_VALUE);
    }
    
    protected Length max_length(int maxLength)
    {
        return new Length(0, maxLength);
    }
    
    protected Range range(int minValue, int maxValue)
    {
        return new Range(minValue, maxValue);
    }
    
    protected Regex regex(String regex)
    {
        return new Regex(regex);
    }
    
    protected Capital capital()
    {
        return new Capital();
    }
}
